﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using StockFinder.DataAccess;
using log4net;
using StockFinder.Model;
using StockFinder.Model.Enumerations;
using StockFinder.Services.Extractors;
using StockFinder.Services.Extractors.Yahoo;
using StockFinder.Indicators;
using StockFinder.Services.PriceAnalysis;
using StockFinder.Services.Extractors.IBD;

namespace Testharness
{
    public partial class MainForm : Form
    {
        private static readonly ILog _Log = LogManager.GetLogger(typeof(MainForm));

        private GeneralDataAccess _GeneralDataAccess = new GeneralDataAccess();
        private SymbolDataAccess _SymbolDataAccess = new SymbolDataAccess();
        private DailyPriceDataAccess _DailyPriceDataAccess = new DailyPriceDataAccess();
        private ResultsDataAccess _ResultsDataAccess = new ResultsDataAccess();
        private IHistoricPricesExtractor _YahooricesExtractor = new YahooHistoricPriceExtractor();
        private IBDDataAccess _IBDDataAccess = new IBDDataAccess();

        public MainForm()
        {
            InitializeComponent();

            _DateFromDatePicker.Value = DateTime.Today.AddMonths(-1);
            _DateToDatePicker.Value = DateTime.Today;
        }

        private void _TruncateAllButton_Click(object sender, EventArgs e)
        {
            //clear out data
            _GeneralDataAccess.TruncateAll();
        }

        private void _DownloadPricesButton_Click(object sender, EventArgs e)
        {
            if (!_PricesBackgroundWorker.IsBusy && _DownloadPricesButton.Text == "Download Prices")
            {
                _PricesBackgroundWorker.RunWorkerAsync();

                _DownloadPricesButton.Text = "Cancel";
            }
            else if (_PricesBackgroundWorker.IsBusy && _DownloadPricesButton.Text == "Cancel")
            {
                _PricesBackgroundWorker.CancelAsync();
            }
        }

        private void _JustBrokenOutButton_Click(object sender, EventArgs e)
        {
            if (!_JustBrokenOutBackgroundWorker.IsBusy && _JustBrokenOutButton.Text == "Just Broken Out")
            {
                _JustBrokenOutBackgroundWorker.RunWorkerAsync();

                _JustBrokenOutButton.Text = "Cancel";
            }
            else if (_JustBrokenOutBackgroundWorker.IsBusy && _JustBrokenOutButton.Text == "Cancel")
            {
                _JustBrokenOutBackgroundWorker.CancelAsync();
            }
        }

        private void _PricesBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            _Log.DebugFormat("Loading symbols");

            List<Symbol> symbols = SymbolDataAccess.GetAllSymbols();

            HistoricPricesExtract result = null;
            Symbol symbol = null;
            int progress = 0;
            TimeSpan timeTaken;
            decimal averageTimeTaken;
            decimal timeLeft;
            DateTime estimatedTimeCompleted;

            _Log.DebugFormat("Looping {0} symbols...", symbols.Count);

            DateTime timeStarted = DateTime.Now;

            for (int index = 0; index < symbols.Count; index++)
            {
                if (_PricesBackgroundWorker.CancellationPending)
                {
                    _Log.DebugFormat("!! Canceling prices download !!");
                    e.Cancel = true;
                    break;
                }

                symbol = symbols[index];

                _Log.DebugFormat("Downloading: {0} ({1})", symbol.Name, symbol.SymbolId);

                result = _YahooricesExtractor.ExtractHistoricPrices(_DateFromDatePicker.Value.Date, _DateToDatePicker.Value.Date, symbol.Name);

                if (result.HistoricPricesExtractResult == HistoricPricesExtractResult.SUCCESS)
                {
                    _Log.DebugFormat("**** Downloaded {0} prices ****", result.DailyPrices.Count);

                    //insert prices
                    DailyPriceDataAccess.InsertDailyPrices(result.DailyPrices);

                    _Log.DebugFormat("Insert prices into database");
                }
                else
                {
                    _Log.DebugFormat("Download failed: {0}", result.Message);
                }

                progress = Convert.ToInt32(((decimal)index / (decimal)symbols.Count) * 100);
                _Log.DebugFormat("{0}%", progress);
                _PricesBackgroundWorker.ReportProgress(progress);

                int counter = index + 1;

                //time diagnostics
                timeTaken = DateTime.Now - timeStarted;
                averageTimeTaken = (decimal)timeTaken.TotalSeconds / (decimal)counter;
                timeLeft = averageTimeTaken * (symbols.Count - counter);
                estimatedTimeCompleted = DateTime.Now.AddSeconds(Convert.ToInt32(timeLeft));

                Invoke((MethodInvoker)delegate { _PricesTimeCompletedLabel.Text = estimatedTimeCompleted.ToString("dd/MM/yy HH:mm:ss"); });
            }

            _Log.DebugFormat("Completed");
        }

        private void _PricesBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            _PricesProgressBar.Value = e.ProgressPercentage;
        }

        private void _PricesBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _PricesProgressBar.Value = 0;
            _DownloadPricesButton.Text = "Download Prices";

            if (e.Cancelled)
            {
                _Log.DebugFormat("Price worker canceled");
            }
            else if (e.Error != null)
            {
                _Log.DebugFormat("Price worker stoped due to error: {0}", e.Error.ToString());
            }
            else
            {
                _Log.DebugFormat("Worker completed sucessfully");
            }
        }

        private void _JustBrokenOutBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            _Log.DebugFormat("Deleting current results");

            _ResultsDataAccess.DeleteAllJustBrokenOut();

            _Log.DebugFormat("Loading symbols...");

            List<Symbol> symbols = SymbolDataAccess.GetAllSymbols();

            _Log.DebugFormat("Looping {0} symbols", symbols.Count);

            List<DailyPrice> prices = null;
            Symbol symbol = null;
            PriceAnalysisResult result = null;
            IPriceAnalyser hasJustBrokenOutPriceAnalyser = new JustBrokenOutPriceAnalyser();
            TimeSpan timeTaken;
            decimal averageTimeTaken;
            decimal timeLeft;
            DateTime estimatedTimeCompleted;
            DateTime timeStarted = DateTime.Now;
            int progress = 0;

            for (int i = 0; i < symbols.Count; i++)
            {
                symbol = symbols[i];

                _Log.DebugFormat("Getting prices for {0} ({1})", symbol.Name, symbol.SymbolId);

                //get prices
                prices = _DailyPriceDataAccess.GetAllPricesBySymbolId(symbol.SymbolId);

                if (prices != null && prices.Count > 0)
                {
                    _Log.DebugFormat("Analysing {0} prices", prices.Count);

                    //find those that have just broken out 30 day base
                    result = hasJustBrokenOutPriceAnalyser.RunAnalysis(prices.OrderByDescending(price => price.PriceDate));

                    if (result.Succeeded)
                    {
                        _Log.DebugFormat("!!!! HAS JUST BROKEN OUT !!!!");

                        _ResultsDataAccess.InsertJustBrokenOut(symbol.Name, result.BaseLength, result.BaseDepth,
                            result.BaseLow, result.BaselowDate, result.PreviousHighDate, result.PreviousHigh, result.MostRecentPriceDate, result.DaysOfPrices, DateTime.Today);
                    }
                    else
                    {

                    }
                }
                else
                {
                    _Log.DebugFormat("No prices");
                }

                progress = Convert.ToInt32(((decimal)i / (decimal)symbols.Count) * 100);
                _Log.DebugFormat("{0}%", progress);
                _JustBrokenOutBackgroundWorker.ReportProgress(progress);

                int counter = i + 1;

                //time diagnostics
                timeTaken = DateTime.Now - timeStarted;
                averageTimeTaken = (decimal)timeTaken.TotalSeconds / (decimal)counter;
                timeLeft = averageTimeTaken * (symbols.Count - counter);
                estimatedTimeCompleted = DateTime.Now.AddSeconds(Convert.ToInt32(timeLeft));

                Invoke((MethodInvoker)delegate { _PriceAnalysisETALabel.Text = estimatedTimeCompleted.ToString("dd/MM/yy HH:mm:ss"); });
            }
        }

        private void _JustBrokenOutBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            _PriceAnalysisProgressBar.Value = e.ProgressPercentage;
        }

        private void _JustBrokenOutBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _PriceAnalysisProgressBar.Value = 0;
            _PriceAnalysisETALabel.Text = string.Empty;
            _JustBrokenOutButton.Text = "Just Broken Out";

            if (e.Cancelled)
            {
                _Log.DebugFormat("Just broken out worker canceled");
            }
            else if (e.Error != null)
            {
                _Log.DebugFormat("Just broken out worker stoped due to error: {0}", e.Error.ToString());
            }
            else
            {
                _Log.DebugFormat("Just broken out worker completed sucessfully");
            }
        }

        private void _IBDDownloadRatingsButton_Click(object sender, EventArgs e)
        {
            IBDSymbolsByCompositeRatingExtractor ibdSymbolsByCompositeRatingExtractor = new IBDSymbolsByCompositeRatingExtractor();

            _Log.DebugFormat("Deleting current IBD ratings...");

            _IBDDataAccess.DeleteAllIBDRatings();

            _Log.DebugFormat("Looping rating indexes..");

            List<IBDRatings> ratings = null;

            for (int i = 1; i < 100; i++)
            {
                _Log.DebugFormat("Downloading ratings for IBD composite {0}", i);

                ratings = ibdSymbolsByCompositeRatingExtractor.GetByCompositeRating(i);

                _Log.DebugFormat("Inserting {0} ratings in database", ratings.Count);

                ratings.ForEach(rating => IBDDataAccess.InsertIBDRating(rating));

                _Log.DebugFormat("Done");
            }

            _Log.DebugFormat("Completed");
        }

        private void _RunButton_Click(object sender, EventArgs e)
        {
            //1. truncate tables
            _Log.DebugFormat("Truncating tables...");

            _GeneralDataAccess.TruncateAll();

            //2. download top 25% IBD symbols
            _Log.DebugFormat("Looping rating indexes..");

            IBDSymbolsByCompositeRatingExtractor ibdSymbolsByCompositeRatingExtractor = new IBDSymbolsByCompositeRatingExtractor();

            List<IBDRatings> ratings = new List<IBDRatings>();

            //download ratings
            for (int i = 99; i >= 75; i--)
            {
                _Log.DebugFormat("Downloading ratings for IBD composite {0}", i);

                ratings.AddRange(ibdSymbolsByCompositeRatingExtractor.GetByCompositeRating(i));
            }

            //3. insert symbol list

            _Log.DebugFormat("Inserting {0} ratings in database", ratings.Count);

            ratings.ForEach(rating =>
            {
                //insert symbol
                //_SymbolDataAccess.InsertSymbol(rating.Symbol);

                //insert symbol
                IBDDataAccess.InsertIBDRating(rating);
            });

            _Log.DebugFormat("Done");

            //3. get price history for symbol - 10 years

            _Log.DebugFormat("Downloading price history for stocks");

            IBDRatings currentRatings = null;
            HistoricPricesExtract result = null;
            int progress = 0;
           
            for (int index = 0; index < ratings.Count; index++)
            {
                currentRatings = ratings[index];

                _Log.DebugFormat("Downloading: {0} ({1})", currentRatings.Symbol,0);

                result = _YahooricesExtractor.ExtractHistoricPrices(_DateFromDatePicker.Value.Date, _DateToDatePicker.Value.Date, 
                    currentRatings.Symbol);

                if (result.HistoricPricesExtractResult == HistoricPricesExtractResult.SUCCESS)
                {
                    _Log.DebugFormat("**** Downloaded {0} prices ****", result.DailyPrices.Count);

                    //insert prices
                    DailyPriceDataAccess.InsertDailyPrices(result.DailyPrices);

                    _Log.DebugFormat("Insert prices into database");
                }
                else
                {
                    _Log.DebugFormat("Download failed: {0}", result.Message);
                }

                progress = Convert.ToInt32(((decimal)index / (decimal)ratings.Count) * 100);
                _Log.DebugFormat("{0}%", progress);
                _PricesBackgroundWorker.ReportProgress(progress);
            }

            //4. run just broken out strategy

            List<DailyPrice> prices = null;
            PriceAnalysisResult priceAnalysisResult = null;
            IPriceAnalyser hasJustBrokenOutPriceAnalyser = new JustBrokenOutPriceAnalyser();

            for (int i = 0; i < ratings.Count; i++)
            {
                currentRatings = ratings[i];

                _Log.DebugFormat("Getting prices for {0} ({1})", currentRatings.Symbol, 0);

                //get prices
                prices = _DailyPriceDataAccess.GetAllPricesBySymbolId(0);

                if (prices != null && prices.Count > 0)
                {
                    _Log.DebugFormat("Analysing {0} prices", prices.Count);

                    //find those that have just broken out 30 day base
                    priceAnalysisResult = hasJustBrokenOutPriceAnalyser.RunAnalysis(prices.OrderByDescending(price => price.PriceDate));

                    if (priceAnalysisResult.Succeeded)
                    {
                        _Log.DebugFormat("!!!! HAS JUST BROKEN OUT !!!!");

                        _ResultsDataAccess.InsertJustBrokenOut(currentRatings.Symbol, priceAnalysisResult.BaseLength,
                            priceAnalysisResult.BaseDepth, priceAnalysisResult.BaseLow, priceAnalysisResult.BaselowDate,
                            priceAnalysisResult.PreviousHighDate, priceAnalysisResult.PreviousHigh, priceAnalysisResult.MostRecentPriceDate, priceAnalysisResult.DaysOfPrices, DateTime.Today);
                    }
                    else
                    {

                    }
                }
                else
                {
                    _Log.DebugFormat("No prices");
                }
            }
        }       
    }
}
